// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//  File: avtInverseGhostZoneFilter.C
// ************************************************************************* //

#include <avtInverseGhostZoneFilter.h>

#include <avtExtents.h>

#include <vtkCellData.h>
#include <vtkDataSet.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkThreshold.h>
#include <vtkUnsignedCharArray.h>
#include <vtkUnstructuredGrid.h>


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter constructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
// ****************************************************************************

avtInverseGhostZoneFilter::avtInverseGhostZoneFilter()
{
}


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter destructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
//  Modifications:
//
// ****************************************************************************

avtInverseGhostZoneFilter::~avtInverseGhostZoneFilter()
{
}


// ****************************************************************************
//  Method:  avtInverseGhostZoneFilter::Create
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
// ****************************************************************************

avtFilter *
avtInverseGhostZoneFilter::Create()
{
    return new avtInverseGhostZoneFilter();
}


// ****************************************************************************
//  Method:      avtInverseGhostZoneFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
// ****************************************************************************

void
avtInverseGhostZoneFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const InverseGhostZoneAttributes*)a;
}


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtInverseGhostZoneFilter with the given
//      parameters would result in an equivalent avtInverseGhostZoneFilter.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
// ****************************************************************************

bool
avtInverseGhostZoneFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(InverseGhostZoneAttributes*)a);
}


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter::ExecuteData
//
//  Purpose:
//      Sends the specified input and output through the InverseGhostZone filter.
//
//  Arguments:
//      in_dr      The input data representation.
//
//  Returns:       The output data representation.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Thu Jan 8 09:27:11 PDT 2004
//
//  Modifications:
//
//    Hank Childs, Fri Aug 27 15:25:22 PDT 2004
//    Rename ghost data array.
//
//    Hank Childs, Thu Mar  2 11:08:43 PST 2006
//    Remove avtRealDims ['6724].
//
//    Hank Childs, Wed Jun 14 13:44:49 PDT 2006
//    Fix memory leak.
//
//    Sean Ahern, Thu Aug 21 14:25:51 EDT 2008
//    When there are no ghost zones, the inverse is a NULL mesh.
//
//    Hank Childs, Wed Dec 29 18:04:21 PST 2010
//    Reimplement routine to allow for selecting individual types of ghost 
//    cells.
//
//    Eric Brugger, Wed Jul 30 18:29:34 PDT 2014
//    Modified the class to work with avtDataRepresentation.
//
//    Eric Brugger, Fri Sep 26 08:48:41 PDT 2014
//    I modified the routine to return a NULL in the case where it previously
//    returned an avtDataRepresentation with a NULL vtkDataSet.
//
//    Kathleen Biagas, Fri Dec 1 2023
//    vtkThreshold::ThresholdBetween has been deprecated for VTK-9, so use
//    the new methods SetUpper/LowerThreshold instead when using VTK-9.
//
// ****************************************************************************

avtDataRepresentation *
avtInverseGhostZoneFilter::ExecuteData(avtDataRepresentation *in_dr)
{
    //
    // Get the VTK data set.
    //
    vtkDataSet *in_ds = in_dr->GetDataVTK();

    vtkDataArray *gz = in_ds->GetCellData()->GetArray("avtGhostZones");
    if (gz == NULL)
    {
        return NULL;
    }

    //
    // Make an output that is identical to the input.
    //
    vtkDataSet *temp_ds = (vtkDataSet *) in_ds->NewInstance();
    temp_ds->ShallowCopy(in_ds);
    temp_ds->GetCellData()->RemoveArray("avtGhostZones");

    unsigned int mask = 0;
    if (atts.GetShowDuplicated())
    {
        int m = 1;
        for (int i = 0 ; i < DUPLICATED_ZONE_INTERNAL_TO_PROBLEM ; i++)
            m *= 2;
        mask |= m;
    }
    if (atts.GetShowEnhancedConnectivity())
    {
        int m = 1;
        for (int i = 0 ; i < ENHANCED_CONNECTIVITY_ZONE ; i++)
            m *= 2;
        mask |= m;
    }
    if (atts.GetShowReducedConnectivity())
    {
        int m = 1;
        for (int i = 0 ; i < REDUCED_CONNECTIVITY_ZONE ; i++)
            m *= 2;
        mask |= m;
    }
    if (atts.GetShowAMRRefined())
    {
        int m = 1;
        for (int i = 0 ; i < REFINED_ZONE_IN_AMR_GRID ; i++)
            m *= 2;
        mask |= m;
    }
    if (atts.GetShowExterior())
    {
        int m = 1;
        for (int i = 0 ; i < ZONE_EXTERIOR_TO_PROBLEM ; i++)
            m *= 2;
        mask |= m;
    }
    if (atts.GetShowNotApplicable())
    {
        int m = 1;
        for (int i = 0 ; i < ZONE_NOT_APPLICABLE_TO_PROBLEM ; i++)
            m *= 2;
        mask |= m;
    }

    int nCells = in_ds->GetNumberOfCells();
    unsigned char *gza = ((vtkUnsignedCharArray*)gz)->GetPointer(0);
    vtkFloatArray *retainThese = vtkFloatArray::New();
    retainThese->SetName("avtRetainThese");
    retainThese->Allocate(nCells);
    for (int i = 0 ; i < nCells ; i++)
    {
        retainThese->InsertNextValue(gza[i] & mask ? 1.0 : 0.0);
    }
    temp_ds->GetCellData()->RemoveArray("avtGhostZones");
    if (temp_ds->GetFieldData()->GetArray("avtRealDims") != NULL)
        temp_ds->GetFieldData()->RemoveArray("avtRealDims");
    temp_ds->GetCellData()->AddArray(retainThese);
    temp_ds->GetCellData()->SetActiveScalars("avtRetainThese");
    
    vtkThreshold *t = vtkThreshold::New();
    t->SetLowerThreshold(0.5);
    t->SetUpperThreshold(1.5);
    t->SetInputArrayToProcess(0,0,0,vtkDataObject::FIELD_ASSOCIATION_CELLS,
                              "avtRetainThese");
    t->SetInputData(temp_ds);
    t->Update();
    vtkDataSet *out_ds = t->GetOutput();
    out_ds->GetCellData()->RemoveArray("avtRetainThese");
    if (in_ds->GetCellData()->GetScalars() != NULL)
        out_ds->GetCellData()->SetActiveScalars(
                                in_ds->GetCellData()->GetScalars()->GetName());
    if (in_ds->GetPointData()->GetScalars() != NULL)
        out_ds->GetPointData()->SetActiveScalars(
                                in_ds->GetPointData()->GetScalars()->GetName());
    retainThese->Delete();
    if (out_ds->GetNumberOfCells() == 0)
    {
        t->Delete();
        return NULL;
    }

    avtDataRepresentation *out_dr = new avtDataRepresentation(out_ds,
        in_dr->GetDomain(), in_dr->GetLabel());

    t->Delete();

    return out_dr;
}


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter::UpdateDataObjectInfo
//
//  Purpose:
//      Indicates the zones no longer correspond to the original problem.
//      (They of course do, but information about the zones, like facelists,
//       no longer apply).
//
//  Programmer: Hank Childs
//  Creation:   January 8, 2004
//
//  Modifications:
//    Brad Whitlock, Thu Aug 18 16:15:30 PDT 2011
//    Clear out the data extents since the ghost values may have different
//    extents than the real data that we've seen so far.
//
//    Brad Whitlock, Mon Apr  7 15:55:02 PDT 2014
//    Add filter metadata used in export.
//    Work partially supported by DOE Grant SC0007548.
//
// ****************************************************************************

void
avtInverseGhostZoneFilter::UpdateDataObjectInfo(void)
{
    GetOutput()->GetInfo().GetValidity().InvalidateZones();

    avtDataAttributes &atts = GetOutput()->GetInfo().GetAttributes();
    if(atts.ValidActiveVariable() && atts.GetVariableDimension() > 0)
    {
        atts.GetOriginalDataExtents()->Clear();
        atts.GetThisProcsOriginalDataExtents()->Clear();
        atts.GetDesiredDataExtents()->Clear();
        atts.GetActualDataExtents()->Clear();
        atts.GetThisProcsActualDataExtents()->Clear();
    }

    atts.AddFilterMetaData("InverseGhostZone");
}


// ****************************************************************************
//  Method: avtInverseGhostZoneFilter::ModifyContract
//
//  Purpose:
//      Request ghost zones if desired.
//
//  Programmer: Cyrus Harrison.
//  Creation:   Thu Jul  8 13:01:33 PDT 2010
//
//  Modifications:
//
//
// ****************************************************************************

avtContract_p
avtInverseGhostZoneFilter::ModifyContract(avtContract_p in_spec)
{
    avtContract_p contract = new avtContract(in_spec);

    if(atts.GetRequestGhostZones())
        contract->GetDataRequest()->SetDesiredGhostDataType(GHOST_ZONE_DATA);

    return contract;
}

